package com.springboot.alibaba.dubbo.consumer.config;

import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * @link https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E5%AE%9A%E4%B9%89%E8%A7%84%E5%88%99
 * @author beiluo
 * Email beiluo@uoko.com
 * @version V1.0
 * @description
 * @date 2019/5/21 10:40
 */
@Component
public class SentinelConfig implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        initFlowQpsRule();
        initDegradeRule();
    }

    /**
     * 流量控制规则 (FlowRule)
     * 流量规则的定义
     * 重要属性：
     *
     * Field	说明	默认值
     * resource	资源名，资源名是限流规则的作用对象
     * count	限流阈值
     * grade	限流阈值类型，QPS 或线程数模式	QPS 模式
     * limitApp	流控针对的调用来源	default，代表不区分调用来源
     * strategy	判断的根据是资源自身，还是根据其它关联资源 (refResource)，还是根据链路入口	根据资源本身
     * controlBehavior	流控效果（直接拒绝 / 排队等待 / 慢启动模式）	直接拒绝
     * 同一个资源可以同时有多个限流规则。
     *
     * 通过代码定义流量控制规则
     * 理解上面规则的定义之后，我们可以通过调用 FlowRuleManager.loadRules() 方法来用硬编码的方式定义流量控制规则，比如：
     */
    private void initFlowQpsRule() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule("test");
        // set limit qps to 20
        rule.setCount(3);
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setLimitApp("default");
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }

    /**
     * 熔断降级规则 (DegradeRule)
     * 熔断降级规则包含下面几个重要的属性：
     *
     * Field	说明	默认值
     * resource	资源名，即限流规则的作用对象
     * count	阈值
     * grade	降级模式，根据 RT 降级还是根据异常比例降级	RT
     * timeWindow	降级的时间，单位为 s
     * 同一个资源可以同时有多个降级规则。
     *
     * 理解上面规则的定义之后，我们可以通过调用 DegradeRuleManager.loadRules() 方法来用硬编码的方式定义流量控制规则。
     */
    private void initDegradeRule() {
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule();
        rule.setResource("test");
        // set threshold RT, 10 ms
        rule.setCount(10);
        rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
        rule.setTimeWindow(10);
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
    }

    /**
     * 系统保护规则 (SystemRule)
     * 规则包含下面几个重要的属性：
     *
     * Field	说明	默认值
     * highestSystemLoad	最大的 load1，参考值	-1 (不生效)
     * avgRt	所有入口流量的平均响应时间	-1 (不生效)
     * maxThread	入口流量的最大并发数	-1 (不生效)
     * qps	所有入口资源的 QPS	-1 (不生效)
     * 理解上面规则的定义之后，我们可以通过调用 SystemRuleManager.loadRules() 方法来用硬编码的方式定义流量控制规则。
     */
    private void initSystemRule() {
        List<SystemRule> rules = new ArrayList<>();
        SystemRule rule = new SystemRule();
        rule.setHighestSystemLoad(10);
        rules.add(rule);
        SystemRuleManager.loadRules(rules);
    }

    /**
     * 很多时候，我们需要根据调用方来限制资源是否通过，这时候可以使用 Sentinel 的黑白名单控制的功能。黑白名单根据资源的请求来源（origin）限制资源是否通过，若配置白名单则只有请求来源位于白名单内时才可通过；若配置黑名单则请求来源位于黑名单时不通过，其余的请求通过。
     *
     * 调用方信息通过 ContextUtil.enter(resourceName, origin) 方法中的 origin 参数传入。
     *
     * 规则配置
     * 黑白名单规则（AuthorityRule）非常简单，主要有以下配置项：
     *
     * resource：资源名，即限流规则的作用对象
     * limitApp：对应的黑名单/白名单，不同 origin 用 , 分隔，如 appA,appB
     * strategy：限制模式，AUTHORITY_WHITE 为白名单模式，AUTHORITY_BLACK 为黑名单模式，默认为白名单模式
     * 示例
     * 比如我们希望控制对资源 test 的访问设置白名单，只有来源为 appA 和 appB 的请求才可通过，则可以配置如下白名单规则：
     */
    private void initAuthorityRule() {
        List<AuthorityRule> rules =new ArrayList<>();
        AuthorityRule rule = new AuthorityRule();
        rule.setResource("test");
        rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
        rule.setLimitApp("appA,appB");
        AuthorityRuleManager.loadRules(rules);
    }
}
